﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.AsyncOperations;
using HybridCLR;

public class AADownloadManager : MonoBehaviour
{
    private List<string> catalogs = new List<string>();
    private int progressIndex = 0;
    private long totalSize = 0;
    private List<IResourceLocator> resourceLocators;

    private void Start()
    {
        StartCoroutine(CheckUpdateAsset());
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            Debug.Log("清理缓存...");
            // 清理缓存
            Caching.ClearCache();
        }
    }

    private IEnumerator CheckUpdateAsset()
    {
        yield return Addressables.InitializeAsync();

        var checkHandler = Addressables.CheckForCatalogUpdates(false);
        yield return checkHandler;

        if (checkHandler.Status == AsyncOperationStatus.Succeeded)
        {
            catalogs = checkHandler.Result;
            if (catalogs != null && catalogs.Count > 0)
            {
                Debug.Log($"有{catalogs.Count}个资源需要更新");
                StartCoroutine(CheckUpdateAssetSize());
            }
            else
            {
                Debug.Log("无可更新内容，直接进入游戏...");
                StartGame();
            }
        }
        else
        {
            Debug.LogError("检测更新失败" + checkHandler.OperationException);
        }

        Addressables.Release(checkHandler);
    }

    private IEnumerator CheckUpdateAssetSize()
    {
        var updateCatalogHandle = Addressables.UpdateCatalogs(catalogs, false);
        yield return updateCatalogHandle;

        if (updateCatalogHandle.Status == AsyncOperationStatus.Succeeded)
        {
            resourceLocators = updateCatalogHandle.Result;
            foreach (var locator in resourceLocators)
            {
                foreach (var item in locator.Keys)
                {
                    Debug.Log(item.ToString());
                }

                var downloadSizeHandle = Addressables.GetDownloadSizeAsync(locator.Keys);
                yield return downloadSizeHandle;

                totalSize += downloadSizeHandle.Result;
            }

            if (totalSize > 0)
            {
                Debug.Log($"获取到的下载大小：{totalSize / 1048576f} M"); // 除2个1024
                progressIndex = 0;
                StartCoroutine(DownloadAsset());
            }
            else
            {
                Debug.Log("无可更新内容");
                StartGame();
            }
        }
        else
        {
            Debug.LogError("更新资源列表失败" + updateCatalogHandle.OperationException);
        }

        Addressables.Release(updateCatalogHandle);
    }

    private IEnumerator DownloadAsset()
    {
        Debug.Log("正在更新资源...");
        //int retryCount = 0; // 当前重试次数
        //int MaxRetries = 3; // 最大重试次数
        //float RetryDelay = 2f; // 重试延迟时间（秒）

        for (int i = progressIndex; i < resourceLocators.Count; i++)
        {
            //retryCount = 0;
            var item = resourceLocators[i];

            //while (retryCount < MaxRetries)
            {
                var downloadHandle = Addressables.DownloadDependenciesAsync(item.Keys, Addressables.MergeMode.Union, false);
                while (!downloadHandle.IsDone)
                {
                    float progress = downloadHandle.PercentComplete;
                    Debug.Log("当前进度:" + progress);
                    yield return null;
                }

                yield return downloadHandle;

                if (downloadHandle.Status == AsyncOperationStatus.Succeeded)
                {
                    progressIndex++;
                    Debug.Log($"下载成功：{item}...");
                    Addressables.Release(downloadHandle);
                    break; // 跳出重试循环
                }
                else
                {
                    //retryCount++;
                    Debug.LogError($"下载失败:{item},err:{downloadHandle.OperationException},下载到第{progressIndex}个资源");
                    Addressables.Release(downloadHandle);
                    //yield return new WaitForSeconds(RetryDelay); // 添加重试延迟
                    //Debug.Log($"重试第{retryCount}次");
                }
            }
        }

        if (progressIndex == resourceLocators.Count)
        {
            Debug.Log("更新完成");
            StartGame();
        }
        else
        {
            Debug.LogError($"资源下载未完成, 下载到第{progressIndex}个资源...");
        }
    }

    private static Assembly _hotUpdateAss;

    private void StartGame()
    {
        LoadMetadataForAOTAssemblies("AOTMetadataDll");

        //#if UNITY_EDITOR
        //        string hotUpdateName = "HotUpdate";
        //        _hotUpdateAss = AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == hotUpdateName);
        //        Type entryType = _hotUpdateAss.GetType("GameEntry");
        //        entryType.GetMethod("StartGame").Invoke(null, null);
        //#else
        //#endif

        string hotUpdateName = "HotUpdate.dll.bytes";
        Debug.Log($"加载资源Key路径：{hotUpdateName}");
        AsyncOperationHandle<TextAsset> handle = Addressables.LoadAssetAsync<TextAsset>(hotUpdateName);
        handle.Completed += (opHandle) =>
        {
            Debug.Log($"加载资源Key状态：{opHandle.Status}");
            if (opHandle.Status == AsyncOperationStatus.Succeeded)
            {
                Debug.Log($"加载资源大小：{opHandle.Result.bytes.Length}");
                _hotUpdateAss = Assembly.Load(opHandle.Result.bytes);

                Type entryType = _hotUpdateAss.GetType("Entry");
                entryType.GetMethod("Start").Invoke(null, null);
            }
            else if (opHandle.Status == AsyncOperationStatus.Failed)
            {
                Debug.LogError($"加载资源失败，资源Key路径：{hotUpdateName}，\n异常 {opHandle.OperationException}");
            }
        };
    }

    private static List<string> AOTMetaAssemblyFiles { get; } = new List<string>()
    {
        "mscorlib.dll.bytes",
        "System.dll.bytes",
        "System.Core.dll.bytes",
        "Unity.ResourceManager.dll.bytes",
    };

    /// <summary>
    /// **给 AOT Metadata 的 dll 添加标签 AOTMetadataDll**
    /// 为aot assembly加载原始metadata， 这个代码放aot或者热更新都行。
    /// 一旦加载后，如果AOT泛型函数对应native实现不存在，则自动替换为解释模式执行
    /// </summary>
    /// <param name="aotMetadataDllLabelName">AA中的AOT元数据dll的标签名</param>
    private static void LoadMetadataForAOTAssemblies(string aotMetadataDllLabelName = "AOTMetadataDll")
    {
        HomologousImageMode mode = HomologousImageMode.SuperSet;
        // 列表可用于释放资源
        List<TextAsset> aotMetadataDll = new List<TextAsset>();
        Addressables.LoadAssetsAsync<TextAsset>("AOTMetadataDll", textAsset =>
        {
            aotMetadataDll.Add(textAsset);
            // 注意，补充元数据是给AOT dll补充元数据，而不是给热更新dll补充元数据。
            // 热更新dll不缺元数据，不需要补充，如果调用LoadMetadataForAOTAssembly会返回错误
            // 加载assembly对应的dll，会自动为它hook。一旦aot泛型函数的native函数不存在，用解释器版本代码
            LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(textAsset.bytes, mode);
            Debug.Log($"LoadMetadataForAOTAssembly:{textAsset.name}. mode:{mode} ret:{err}");
        });
    }

    /// <summary>
    /// 为aot assembly加载原始metadata， 这个代码放aot或者热更新都行。
    /// 一旦加载后，如果AOT泛型函数对应native实现不存在，则自动替换为解释模式执行
    /// </summary>
    private void LoadMetadataForAOTAssemblies()
    {
        // 注意，补充元数据是给AOT dll补充元数据，而不是给热更新dll补充元数据。
        // 热更新dll不缺元数据，不需要补充，如果调用LoadMetadataForAOTAssembly会返回错误
        HomologousImageMode mode = HomologousImageMode.SuperSet;
        foreach (var aotDllName in AOTMetaAssemblyFiles)
        {
            byte[] dllBytes = ReadBytesFromStreamingAssets(aotDllName);
            // 加载assembly对应的dll，会自动为它hook。一旦aot泛型函数的native函数不存在，用解释器版本代码
            LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
            Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
        }
    }

    private byte[] ReadBytesFromStreamingAssets(string abName)
    {
        Debug.Log($"ReadAllBytes name: {abName}");
#if UNITY_ANDROID
                AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.PrivacyActivity");
                //jc.CallStatic<byte[]>("getFromAssets", name);
                AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
                byte[] oldBytes = jo.Call<byte[]>("getFromAssets", abName);
                return oldBytes;

                #region 在Android工程中添加读取方法

                // import java.io.File;
                // import java.io.FileInputStream;
                // import java.io.FileNotFoundException;
                // import java.io.FileOutputStream;
                // import java.io.IOException;
                // import java.io.InputStream;

                // public byte[] getFromAssets(String fileName) {
                //     Log.e("****", "getFromAssets:" + fileName);
                //     try {
                //         //得到资源中的Raw数据流
                //         InputStream in = getResources().getAssets().open(fileName);
                //         //得到数据的大小
                //         int length = in.available();
                //
                //         byte[] buffer = new byte[length];
                //             //读取数据
                //             in.read(buffer);
                //             //依test.txt的编码类型选择合适的编码，如果不调整会乱码
                //             //res = EncodingUtils.getString(buffer, "BIG5");
                //             //关闭
                //             in.close();
                //
                //         return buffer;
                //     } catch (Exception e) {
                //         e.printStackTrace();
                //         return null;
                //     }
                // }

                #endregion

#else
        byte[] oldBytes = File.ReadAllBytes(Application.streamingAssetsPath + "/" + abName);
        return oldBytes;
#endif
    }
}